package geektime.spring.springbucks.waiter.service;

import geektime.spring.springbucks.waiter.controller.exception.RollbackException;
import geektime.spring.springbucks.waiter.mapper.CoffeeMapper;
import geektime.spring.springbucks.waiter.model.Coffee;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.RowBounds;
import org.joda.money.Money;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class CoffeeService {

    private static final String CACHE = "springbucks-coffee";

    @Resource
    private RedisTemplate<String, Coffee> redisTemplate;

    @Resource
    private CoffeeMapper coffeeMapper;

    @Transactional(rollbackFor = Exception.class)
    public Coffee saveCoffee(String name, Money price) {
        Coffee coffee = Coffee.builder().name(name).price(price).build();
        coffeeMapper.save(coffee);
        return coffee;
    }

    @Transactional(rollbackFor = Exception.class)
    public Coffee saveCoffeeRollBackTest(String name, Money price) throws RollbackException {
        Coffee coffee = Coffee.builder().name(name).price(price).build();
        coffeeMapper.save(coffee);
        if(price.getAmountMajorLong()<20){
            throw new RollbackException();
        }
        return coffee;
    }

    public List<Coffee> getAllCoffee() {
        return coffeeMapper.findAll();
    }

    public List<Coffee> pageQueryWithRowBounds(Integer offSet,Integer limit) {
        return coffeeMapper.findAllWithRowBounds(new RowBounds(offSet,limit));
    }

    public List<Coffee> pageQueryWithParam(Integer pageNum,Integer pageSize) {
        return coffeeMapper.findAllWithParam(pageNum,pageSize);
    }

    public Coffee getCoffee(Long id) {
        return coffeeMapper.findById(id);
    }

    public Coffee getCoffee(String name) {
        HashOperations<String, String, Coffee> hashOperations = redisTemplate.opsForHash();
        if (redisTemplate.hasKey(CACHE) && hashOperations.hasKey(CACHE, name)) {
            log.info("Get coffee {} from Redis.", name);
            return hashOperations.get(CACHE, name);
        }

        Coffee coffee = coffeeMapper.findByName(name);
        if(Objects.nonNull(coffee)){
            log.info("Put coffee {} to Redis.", name);
            hashOperations.put(CACHE, name, coffee);
            redisTemplate.expire(CACHE, 5000, TimeUnit.MINUTES);
        }

        return coffee;
    }

    public List<Coffee> getCoffeeByName(List<String> names) {
        return coffeeMapper.findByNameInOrderById(names);
    }
}
